package com.mit.service.core.aop;

import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.Date;

import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.log4j.Logger;
import org.springframework.aop.MethodBeforeAdvice;

import com.mit.common.dto.AbstractDTO;
import com.mit.common.exception.ConcurrencyException;
import com.mit.common.exception.InappropriateDefinitionException;
import com.mit.config.FormatPattern;
import com.mit.service.core.dao.ITimestampDAO;

public class TimestampValidationAdvice implements MethodBeforeAdvice {

	Logger logger = Logger.getLogger(TimestampValidationAdvice.class);
	private ITimestampDAO timestampDAO;
	
	@Override
	public void before(Method arg0, Object[] arg1, Object arg2)
			throws Throwable {
		AbstractDTO dto = null;
		for(Object o : arg1){
			if(o instanceof AbstractDTO)
				dto = (AbstractDTO) o;
		}
		
		if(dto == null){
			logger.info("[Opti-lock] No DTO found in arguments.");
			return;
		}
		
		String className = dto.getClass().getName();
		String tableName = DtoTableMap.getTableName(className);
		// Throw exception if DTO is not mapped
		if(tableName == null){
			logger.error("[Opti-Lock] Table is not mapped in 'dto-table-mapping.xml', DTO: " + className);
			throw new InappropriateDefinitionException(
					"Table is not mapped! Please check dto-table-mapping.xml. DTO: "
							+ className);
		}
		
		Integer id = dto.getId();
		if(id != null){
			//check the timestamp before updating
			Date timestamp = dto.getLastUpdatedTime();
			if(timestamp == null){
				logger.error("[Opti-Lock] No timestamp in the object to be updated!");
				throw new ConcurrencyException("Timestamp in the DTO to be updated is NULL!");
			}else{
				Date lastUpdatedTime = timestampDAO.retrieveTimestamp(dto.getId(), tableName);
				//Throw exception when the timestamp is not the same.
				if(!lastUpdatedTime.equals(timestamp)){
					String err = "[Opti-Lock] Timestamp is not match. DTO: " 
						+ DateFormatUtils.format(timestamp, FormatPattern.getFullDateTimePattern()) 
						+ ", DB: " 
						+ DateFormatUtils.format(lastUpdatedTime, FormatPattern.getFullDateTimePattern()) ;
					logger.error(err);
					throw new ConcurrencyException(err);
				
				}
			}
		}
	}

	public void setTimestampDAO(ITimestampDAO timestampDAO) {
		this.timestampDAO = timestampDAO;
	}


}
